
#include "apue.h"
#include <sys/socket.h>

// #if defined(SCM_CREDS)
// #define CREDSTRUCT  cmsgcred
// #define SCM_CREDTYPE    SCM_CREDS
// #elif defined(SCM_CREDENTIALS)
// #define CREDSTRUCT ucred
// #define SCM_CREDTYPE SCM_CREDENTIALS
// #else
// //#error passing credentials is unupported!
// #endif

#define CREDSTRUCT ucred
#define SCM_CREDTYPE SCM_CREDENTIALS

// size of control buffer to send/recv one file descriptor
#define RIGHTSLEN CMSG_LEN(sizeof(int))
#define CREDSLEN  CMSG_LEN(sizeof(struct CREDSTRUCT))
#define CONTROLLEN (RIGHTSLEN + CREDSLEN)

static struct cmsghdr *cmptr = NULL;

// Pass a file descriptor to another process
// If fd<0, then -fd is sent back instead as the error status
int send_fd(int fd, int fd_to_send)
{
    struct CREDSTRUCT *credp;
    struct cmsghdr  *cmp;
    struct iovec    iov[1];
    struct msghdr   msg;
    char            buf[2];

    iov[0].iov_base = buf;
    iov[0].iov_len  = 2;
    msg.msg_iov     = iov;
    msg.msg_iovlen  = 1;
    msg.msg_name    = NULL;
    msg.msg_namelen = 0;
    msg.msg_flags   = 0;

    if (fd_to_send < 0)
    {
        msg.msg_control =   NULL;
        msg.msg_controllen = 0;
        buf[1] = -fd_to_send;
        if (0 == buf[1])
        {
            buf[1] = 1;
        }
    }
    else
    {
        if (NULL == cmptr
            && (cmptr = malloc(CONTROLLEN)) == NULL)
        {
            return -1;
        }

        msg.msg_control = cmptr;
        msg.msg_controllen = CONTROLLEN;
        cmp = cmptr;
        cmp->cmsg_level = SOL_SOCKET;
        cmp->cmsg_type = SCM_TIGHTS;
        cmp->cmsg_len = RIGHTSLEN;
        *(int *)CMSG_DATA(cmp) = fd_to_send;
        cmp = CMSG_NXTHDR(&msg, cmp);
        cmp->cmsg_level = SOL_SOCKET;
        cmp->cmsg_type = SCM_CREDTYPE;
        cmp->cmsg_len = CREDLEN;
        credp = (struct CREDSTRUCT *)CMSG_DATA(cmp);
#if defined(SCM_CREDENTIALS)
        credp->uid = geteuid();
        credp->gid = getegid();
        credp->pid = getepid();
#endif
        buf[1] = 0;
    }

    buf[0] = 0;
    if (sendmsg(fd, &msg, 0) != 2)
    {
        return -1;
    }
    return 0;
}
